package view.commonview;

import java.awt.Color;

import java.awt.Font;

import java.text.DateFormat;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JPanel;
import javax.swing.JTextArea;

import view.UtilityConstants;

import controller.commoncontroller.UtilityDialogs;
import locsi.UserSettings;
import model.LogicType;
import model.editormodel.Check;
import model.editormodel.CircuitModel.CircuitInformation;
import model.editormodel.EditorModel;
import model.editormodel.ErrorMessage;
import model.editormodel.ErrorMessage.ErrorType;
import model.editormodel.LocsiParser;

/**
 * <b>Erbt:</b> <code>JPanel</code> <br>
 * <b>Implementiert:</b> <code>Observer</code> <br>
 * <b>In Verbindung stehende Klassen:</b> <code>Check, EditorModel</code> <br>
 * <b>Ueberwachte Klasse: </b> <code>Check</code> <br>
 * <b>Funktion: </b> <br>
 * Ausgabefeld fuer die Ausgabe der Check-Ergebnisse bzw. der Simulationsdaten <br>
 * Ausgabefeld fuer die Schaltungsinformation <br>
 * <br>
 * Das Ausgabefeld wird von den Klassen <code>Check</code> und <code>
 * CircuitInfoListener </code> gespeist. Dabei ueberwacht diese Klasse die
 * <code>Check</code> Klasse.
 * 
 * @author Team Locsi
 * 
 */

@SuppressWarnings("serial")
public class InfoArea extends JPanel implements Observer {

	private EditorModel eModel;
	private JTextArea tArea;
	private GregorianCalendar now;
	private DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
	private DateFormat df2 = DateFormat.getTimeInstance(DateFormat.SHORT);
	private String sep = "-----------------------------------------------------------";
	private String sep2 = "###########################################################";
	private int freeSpace = 50;
	private int checkCount = 3;
	private int checkOk = 0;
	private String output;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>InfoArea</code>-Objekt. Dabei
	 * wird die <code>InfoArea</code> beim <code>Check</code>-Objekt als
	 * <code>Observer</code> angemeldet.
	 * 
	 * @param eModel
	 *            <code>EditorModel</code> - Referenz auf das
	 *            <code>EditorModel</code>
	 */
	public InfoArea(EditorModel eModel) {
		this.eModel = eModel;
		tArea = new JTextArea(6, 75);
		tArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11));
		tArea.setEditable(false);
		tArea.setAutoscrolls(true);
		this.add(tArea);
		this.setBackground(Color.WHITE);
		tArea.setBackground(Color.WHITE);
		this.eModel.getChecker().addObserver(this);
		this.eModel.getParser().addObserver(this);
		now = new GregorianCalendar();
		if (UserSettings.loadingWasSuccessful) {
			this.tArea.append("Einstellungen erfolgreich geladen!"
					+ UtilityConstants.NEWLINE + UtilityConstants.NEWLINE);
		} else {
			this.tArea
					.append("Einstellungen konnten nicht gelesen werden. Standardeinstellungen geladen!"
							+ UtilityConstants.NEWLINE
							+ UtilityConstants.NEWLINE);
		}
		this.tArea.append("Protokoll von LOCSI am " + df.format(now.getTime())
				+ UtilityConstants.NEWLINE);
		this.tArea.append(sep + UtilityConstants.NEWLINE);
	}

	/**
	 * Diese Methode stellt die Informationen ueber die Check-Ergebnisse aus dem
	 * uebergebenen Objekt zusammen, die dann in der <code>InfoArea</code>
	 * ausgegeben werden.
	 */
	@Override
	public void update(Observable o, Object arg) {

		if (o instanceof Check) {
			if (arg instanceof ErrorMessage) {
				ErrorType eType = ((ErrorMessage) arg).getErrorType();
				switch (eType) {

				case EMPTY_CIRCUIT:
					tArea.append(UtilityConstants.NEWLINE + sep2
							+ UtilityConstants.NEWLINE);
					tArea.append(getTimeStamp() + " Starten der Checks..."
							+ UtilityConstants.NEWLINE);
					tArea.append(sep2 + UtilityConstants.NEWLINE);
					String output1 = " Testen ob Schaltung nicht leer:";
					tArea.append(output1 + createFreeSpace(output1.length())
							+ "[FEHLER]" + UtilityConstants.NEWLINE);
					break;

				case NO_EMPTY_CIRCUIT:
					tArea.append(UtilityConstants.NEWLINE + sep2
							+ UtilityConstants.NEWLINE);
					tArea.append(getTimeStamp() + " Starten der Checks..."
							+ UtilityConstants.NEWLINE);
					tArea.append(sep2 + UtilityConstants.NEWLINE);
					output1 = " Testen ob Schaltung nicht leer:";
					tArea.append(output1 + createFreeSpace(output1.length())
							+ "[OK]" + UtilityConstants.NEWLINE);
					checkOk++;
					break;

				case MISSING_INPUT_CONNECTION:

					output1 = " Testen ob alle Eingänge belegt sind:";
					tArea.append(output1 + createFreeSpace(output1.length())
							+ "[FEHLER]" + UtilityConstants.NEWLINE);
					break;

				case INPUT_CHECK_OKAY:

					output1 = " Testen ob alle Eingänge belegt sind:";
					tArea.append(output1 + createFreeSpace(output1.length())
							+ "[OK]" + UtilityConstants.NEWLINE);
					checkOk++;
					break;

				case MISSING_SWITCH_CLOCK:
					output1 = " Testen ob alle Teilnetze Schalter Lampen haben:";
					tArea.append(output1 + createFreeSpace(output1.length())
							+ "[FEHLER]" + UtilityConstants.NEWLINE);
					tArea.append(sep + UtilityConstants.NEWLINE);
					tArea.append(" Es waren " + checkOk + " von " + checkCount
							+ " Tests erfolgreich" + UtilityConstants.NEWLINE
							+ UtilityConstants.NEWLINE);
					tArea.setCaretPosition(tArea.getDocument().getLength());
					checkOk = 0;
					break;

				case SUBNET_CHECK_OKAY:
					output1 = " Testen ob alle Teilnetze Schalter Lampen haben:";
					tArea.append(output1 + createFreeSpace(output1.length())
							+ "[OK]" + UtilityConstants.NEWLINE);
					checkOk++;
					tArea.append(sep + UtilityConstants.NEWLINE);
					tArea.append(" Es waren " + checkOk + " von " + checkCount
							+ " Tests erfolgreich" + UtilityConstants.NEWLINE
							+ UtilityConstants.NEWLINE);
					tArea.setCaretPosition(tArea.getDocument().getLength());
					checkOk = 0;
					break;

				default:
					break;

				}
				tArea.setCaretPosition(tArea.getDocument().getLength());
			}
		} else if (o instanceof LocsiParser) {
			this.tArea.append(getTimeStamp() + (String) arg
					+ UtilityConstants.NEWLINE);
			tArea.setCaretPosition(tArea.getDocument().getLength());
		}
	}

	/**
	 * Diese Methode holt sich vom <code>EditorModel</code> Informationen ueber
	 * die Schaltung und gibt diese in der <code>InfoArea</code> aus.
	 */
	public void showCircuitInfo() {
		Map<LogicType, Integer> circuitInfo = eModel.getCircuitModel()
				.getNumberOfEachLogicType();
		CircuitInformation cInfo = eModel.getCircuitModel().new CircuitInformation(
				eModel.getCircuitModel());
		int sum = 0;

		tArea.append(UtilityConstants.NEWLINE + sep2 + UtilityConstants.NEWLINE);
		tArea.append(getTimeStamp() + " Schaltungsinformation"
				+ UtilityConstants.NEWLINE);
		tArea.append(sep2 + UtilityConstants.NEWLINE + UtilityConstants.NEWLINE);

		tArea.append("Bauteile: " + UtilityConstants.NEWLINE);
		tArea.append(sep + UtilityConstants.NEWLINE);

		for (Map.Entry<LogicType, Integer> m : circuitInfo.entrySet()) {
			sum = sum + m.getValue();
			output = m.getKey() + " : ";
			tArea.append(output + createFreeSpace(output.length())
					+ m.getValue() + UtilityConstants.NEWLINE);
		}

		tArea.append(sep + UtilityConstants.NEWLINE);
		output = "Gesamtanzahl:   ";
		tArea.append(output + createFreeSpace(output.length()) + sum
				+ UtilityConstants.NEWLINE + UtilityConstants.NEWLINE);

		output = "Maximale Tiefe der Schaltung: ";
		String maxDepth = "" + cInfo.getMaxDepth();
		if (cInfo.getMaxDepth() == -1) {
			maxDepth = "-";
		}

		tArea.append(output + createFreeSpace(output.length()) + maxDepth
				+ UtilityConstants.NEWLINE);

		output = "Minimale Tiefe der Schaltung: ";
		String minDepth = "" + cInfo.getMinDepth();
		if (cInfo.getMinDepth() == -1) {
			minDepth = "-";
		}

		tArea.append(output + createFreeSpace(output.length()) + minDepth
				+ UtilityConstants.NEWLINE + UtilityConstants.NEWLINE);

		output = "Anzahl der Subnetze: ";
		List<Integer> subnets = eModel.getCircuitModel().getNumberOfSubnets();
		tArea.append(output + createFreeSpace(output.length())
				+ subnets.get(0).intValue() + UtilityConstants.NEWLINE);
		for (int i = 1; i < subnets.size(); i++) {
			tArea.append("    " + i + ". Subnetz: " + subnets.get(i) + UtilityConstants.NEWLINE);
		}
		tArea.append(UtilityConstants.NEWLINE);

		tArea.setCaretPosition(tArea.getDocument().getLength());
	}

	/**
	 * Speichert den aktuellen Inhalt der InfoArea in einer Textdatei ab
	 */
	public void saveInfoText() {
		UtilityDialogs.saveDialog(tArea, UtilityDialogs.TXT_EXTENSION);

	}

	/**
	 * Loescht den Text aus dem Infobereich
	 */
	public void clearInfoArea() {
		this.tArea.setText("");
		now = new GregorianCalendar();
		this.tArea.append("Protokoll von LOCSI am " + df.format(now.getTime())
				+ UtilityConstants.NEWLINE);
		this.tArea.append(sep + UtilityConstants.NEWLINE);
	}

	/**
	 * Fuegt einen <code>String</code> und eine Leerzeile zum
	 * Informationsbereich hinzu.
	 * 
	 * @param info
	 *            <code>String</code> - Informationen die zum
	 *            Informationsbereich hinzugefuegt werden
	 */
	public void addInfoText(String info) {
		this.tArea.append(getTimeStamp() + info + UtilityConstants.NEWLINE);
		tArea.setCaretPosition(tArea.getDocument().getLength());
	}

	/**
	 * Erzeugt einen String mit geeigneter Laenge fuer die Skalierung der Werte
	 * in der TextArea
	 * 
	 * @param length
	 *            Laenge des bisherigen Strings
	 * @return <code>String</code> - genuegend viele Leerzeichen
	 */
	private String createFreeSpace(int length) {
		String space = "";
		int neededSpace = freeSpace - length;

		for (int i = 0; i < neededSpace; i++) {
			space = space + " ";
		}
		return space;
	}

	/**
	 * Hilfsmethode, die den aktuellen Zeitstempel zurueckgibt
	 * 
	 * @return <code>String</code> - den aktuellen Zeitstempel
	 */
	private String getTimeStamp() {
		now = new GregorianCalendar();
		return "[" + df2.format(now.getTime()) + "] ";
	}
}